Skip to content

Cache for git remote - #1882

Open
jrd wants to merge 1 commit into
firecow:masterfrom
jrd:cache_for_git_remote
Open

Cache for git remote#1882
jrd wants to merge 1 commit into
firecow:masterfrom
jrd:cache_for_git_remote

Conversation

@jrd

@jrd jrd commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Based on #1836 it allows to keep some cache for remote refs so it’s a bit faster.


Summary by cubic

Speeds up include processing by caching identical git ls-remote calls and preventing races by writing the component parse cache only after remote downloads finish.

  • Performance

    • Add in-memory cache for git ls-remote results keyed by the full command; cleared via ParserIncludes.resetCount().
    • Defer componentParseCache.set until after remote component download completes to avoid parallel resolution races.
  • Refactors

    • Replace exported getGitRemoteInfo with ParserIncludes.getGitRemoteInfo and update call sites.

Written for commit eeea380. Summary will update on new commits.

Review in cubic

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 issues found across 9 files

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread src/parser.ts
Comment thread src/parser-includes.ts
@jrd
jrd force-pushed the cache_for_git_remote branch from f5279b8 to cf7ad82 Compare June 22, 2026 11:10
@firecow

firecow commented Jun 22, 2026

Copy link
Copy Markdown
Owner

I think #1836 already covers the component.* side nicely, so I'm going to close this one to keep things simple.
If performance issues arise, we can revisit later.

@firecow firecow closed this Jun 22, 2026
@jrd

jrd commented Jun 22, 2026

Copy link
Copy Markdown
Contributor Author

Well @firecow . Here is one of my .gitlab-ci.yml:

include:
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/security@0.8
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/python-linter@0.8
    inputs:
      system-packages: 'libgc++'
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/python-i18n@0.8
    inputs:
      system-packages: 'libgc++'
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/python-tests@0.8
    inputs:
      system-packages: 'libgc++'
      future-enable: true
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/js-linter@0.8
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/js-i18n@0.8
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/js-tests@0.8
    inputs:
      future-enable: true
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/total-coverage@0.8
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/docker-test@0.8
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/tag@0.8
    inputs:
      needs:
        - security
        - secret
        - python-linter
        - python-i18n
        - python-tests
        - js-linter
        - js-i18n
        - js-tests
        - docker-test
  - component: $CI_SERVER_FQDN/systracorp/hq/infra/ci/docker-publish@0.8

# extra parameters to use a postgresql service
python-tests: &python-pg-svc
  services:
    - name: postgres:17-alpine
      alias: db
      variables:
        POSTGRES_DB: syspro
        POSTGRES_USER: syspro
        POSTGRES_PASSWORD: syspro
  variables:
    DB_HOST: db
python-tests:future:
  <<: *python-pg-svc

Without this patch, it takes 19 seconds to run gitlab-ci-local --list.

With it, 3 seconds.

This is with the cache already there for both.

Multiple component at the same version from the same catalog is the reason this takes a long time without this optimization.

@firecow

firecow commented Jun 22, 2026

Copy link
Copy Markdown
Owner

Holy christ 😄

Ok, I'm reopening. Jesus, what a pipeline.

@firecow firecow reopened this Jun 22, 2026
@firecow

firecow commented Jun 22, 2026

Copy link
Copy Markdown
Owner

@jrd rebase onto master now that #1836 landed

Comment thread src/parser-includes.ts Outdated
@jrd
jrd force-pushed the cache_for_git_remote branch from 5652d2b to 1c3a1fd Compare June 25, 2026 10:54
@jrd
jrd requested a review from firecow June 30, 2026 23:53
Comment thread src/parser-includes.ts Outdated
Comment on lines 118 to 124
promises.push((async (componentParseCache: Map<number, ParsedComponent>, component: ParsedComponent, opts: ParserIncludesInitOptions) => {
if (!component.isLocal) {
await this.downloadIncludeComponent(opts, component.projectPath, component.effectiveRef, component.componentPath);
}
componentParseCache.set(index, component);
})(componentParseCache, component, opts));
}

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
promises.push((async (componentParseCache: Map<number, ParsedComponent>, component: ParsedComponent, opts: ParserIncludesInitOptions) => {
if (!component.isLocal) {
await this.downloadIncludeComponent(opts, component.projectPath, component.effectiveRef, component.componentPath);
}
componentParseCache.set(index, component);
})(componentParseCache, component, opts));
}
promises.push((async () => {
if (!component.isLocal) {
await this.downloadIncludeComponent(opts, component.projectPath, component.effectiveRef, component.componentPath);
}
componentParseCache.set(index, component);
})());

Comment thread src/parser-includes.ts
Comment on lines +262 to +269
const cmdStr = cmdArgs.join(" ");
let info = this.gitRemoteInfoCache[cmdStr];
if (!(cmdStr in this.gitRemoteInfoCache)) {
info = Utils.syncSpawn(cmdArgs).stdout;
this.gitRemoteInfoCache[cmdStr] = info;
}
return info;
};

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const cmdStr = cmdArgs.join(" ");
let info = this.gitRemoteInfoCache[cmdStr];
if (!(cmdStr in this.gitRemoteInfoCache)) {
info = Utils.syncSpawn(cmdArgs).stdout;
this.gitRemoteInfoCache[cmdStr] = info;
}
return info;
};
const cmdStr = cmdArgs.join(" ");
if (!(cmdStr in this.gitRemoteInfoCache)) {
this.gitRemoteInfoCache[cmdStr] = Utils.syncSpawn(cmdArgs).stdout;
}
return this.gitRemoteInfoCache[cmdStr];
}

Comment thread src/parser-includes.ts Outdated
if (gitRemoteMatch?.groups == null) throw new Error(`This is a bug, please create a github issue if this is something you're expecting to work. input: ${component}`);
const {domain, projectPath, port, componentName, ref} = gitRemoteMatch.groups;
const isLocalComponent = projectPath === `${gitData.remote.group}/${gitData.remote.project}` && ref === gitData.commit.SHA;
const parserIncludes = this; // eslint-disable-line @typescript-eslint/no-this-alias

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const parserIncludes = this; // eslint-disable-line @typescript-eslint/no-this-alias

Comment thread src/parser-includes.ts Outdated
if (this.reference == "~latest" || semanticVersionRangesPattern.test(this.reference)) {
// https://docs.gitlab.com/ci/components/#semantic-version-ranges
const stdout = getGitRemoteInfo(this, "--tags");
const stdout = parserIncludes.getGitRemoteInfo(this, "--tags");

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const stdout = parserIncludes.getGitRemoteInfo(this, "--tags");
const stdout = ParserIncludes.getGitRemoteInfo(this, "--tags");

Comment thread src/parser-includes.ts Outdated
this._cache.sha = this.effectiveRef;
} else {
const stdout = getGitRemoteInfo(this);
const stdout = parserIncludes.getGitRemoteInfo(this);

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const stdout = parserIncludes.getGitRemoteInfo(this);
const stdout = ParserIncludes.getGitRemoteInfo(this);

@firecow firecow left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The 5 suggestions must be batched. Deleting the alias line alone breaks the build; lines 300 and 326 still reference parserIncludes.

Also: the diff still contains the component.* work #1836 superseded; the rebase you asked for should reduce it to just the caching change.

Comment thread src/parser-includes.ts

export class ParserIncludes {
private static count: number = 0;
private static gitRemoteInfoCache: Record<string, string> = {};

@firecow firecow Jul 31, 2026

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Never cleared, though resetCount() sits right beside it for count. Stale git ls-remote results could leak between runs in the test suite. Broke nothing I ran, so your call whether it matters for a single-shot CLI.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I can reinit the remote info cache in the resetCount method. That would be good enough I think.

@jrd
jrd force-pushed the cache_for_git_remote branch from 1c3a1fd to eeea380 Compare August 5, 2026 15:49
@jrd

jrd commented Aug 6, 2026

Copy link
Copy Markdown
Contributor Author

Should be good now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants